AWS Managed Rules for AWS WAF を構成するCloudFormationテンプレートを作ってみた
こんにちは。 ご機嫌いかがでしょうか。 "No human labor is no human error" が大好きな吉井 亮です。
2019年11月25日(アメリカ時間) に AWS Managed Rules for AWS WAF が公開されています。 AWS WAF 導入へのハードルがかなり低くなってきたと感じています。 弊社カジもアップデート記事を書いています。
AWS Managed Rules for AWS WAF をデプロイする CloudFormation テンプレートを作成してみました。 このテンプレートを実行すると以下のようなリソースがデプロイされます。
ルール
テンプレートを実行するとこのようなルールが入ります。
ルール名 | アクション | 優先度 |
---|---|---|
Custom-IPaddress-BlackList | Count | 0 |
AWS-AWSManagedRulesAmazonIpReputationList | Count | 1 |
AWS-AWSManagedRulesCommonRuleSet | Count | 2 |
AWS-AWSManagedRulesKnownBadInputsRuleSet | Count | 3 |
AWS-AWSManagedRulesLinuxRuleSet | Count | 4 |
AWS-AWSManagedRulesUnixRuleSet | Count | 5 |
Custom-Ratebased | Count | 6 |
Custom-IPaddress-BlackList
接続を拒否する IP アドレスを指定します。 これはマネージドルールではなくユーザー管理のルールです。
AWS-AWSManagedRulesAmazonIpReputationList
通常ボットやその他の脅威に関連付けられている IP アドレスをブロックする場合に役立ちます。 ここの IP アドレスは AWS 管理です。
AWS-AWSManagedRulesCommonRuleSet
Web アプリケーション防御の一般的なルールが含まれています。 OWASP 出版物や CVE で解説されている脆弱性を含んでいます。 AWS 管理のルールです。
AWS-AWSManagedRulesKnownBadInputsRuleSet
脆弱性や悪用のあるパターンに報告されているインプットをブロックするルールが含まれています。 AWS 管理のルールです。
AWS-AWSManagedRulesLinuxRuleSet
Linux 固有の LFI 攻撃を防ぐルールが含まれています。 AWS 管理のルールです。 次の AWS-AWSManagedRulesUnixRuleSet と組み合わせて使用します。
AWS-AWSManagedRulesUnixRuleSet
POSIX 固有の LFI 攻撃を防ぐルールが含まれています。 AWS 管理のルールです。
Custom-Ratebased
5分間で100リクエスト (調整可能) 以上送信してくる IP アドレスをブロックします。 このルールには IP アドレスホワイトリストが含まれており ホワイトリストに載っている IP アドレスはリクエスト数に関係なく無条件に許可します。 ホワイトリストには自社拠点などを指定します。
やってみた
CloudFormation スタック
本エントリの末に記載した CloudFormation テンプレートをローカルに保存してカスタマイズします。
カスタマイズしたテンプレートを使って CloudFormation スタックを作成します。
値 | 説明 |
---|---|
スタックの名前 | 任意の名称 |
BucketName | WAF ログを保存する S3 バケット名。{BucketName}-{アカウントID} になります。 |
WebACLName | WebACL の名称です。任意の名称を指定 |
WAF v2 ロギング
しばらくするとリソースが作成されます。 マネジメントコンソールで WebACL を開いてみます。 CloudFormation スタックを流したリージョンを選択すると無事に WebACL が作成されていることを確認できると思います。
その WebACL をクリックして、Logging and metrics タブを開きます。 Logging が Disabled になっています。 Enable logging をクリックします。
Amazon Kinesis Data Firehose Delivery Stream のドロップダウンから aws-waf-logs-リージョン-WebACL名称 を選択します。 Redacted Fields を任意で選択します。 Enable logging をクリックします。
関連付け
関連付けをします。 同じく WebACL の画面で Associated AWS resources タブを開きます。
Add AWS Resources をクリックします。
関連付けたいリソースを選択します。 Add をクリックします。
アクションの変更
テンプレートを使って作成した WebACL のルールは アクションがカウントになっています。 この状態で十分なテストを行って過検知がないことの確認や 細かい修正を行ってアクションをブロックなどに変更します。
CloudFormation テンプレート
テンプレートはカスタマイズしてお使いください。
カスタマイズ1
AWS::WAFv2::WebACL のプロパティで scope が REGIONAL になっています。 これは ALB または API Gateway 用のテンプレートです。 CloudFront 用の WebACL を作る場合は CLOUDFRONT へ変更します。
カスタマイズ2
IPWhiteList には無条件で許可する IP アドレスを IPBlackList には接続を拒否する IP アドレスを記述ください
カスタマイズ3
WebACL に付けるルールは要件に合わせて増減してください。 AWS 管理のルール一覧は こちら です。
カスタマイズ4
ルール名 Custom-Ratebased の設定値 limit: 100 を 要件に合わせて調整してください。
AWSTemplateFormatVersion: 2010-09-09 Description: WAF v2 Web ACL Parameters: WebACLName: Description: The Name of WebACL. Type: String BucketName: Type: String Description: The name for the bucket. Default: waf-logs Resources: ### Waf logs bucket S3Backet: Type: "AWS::S3::Bucket" Properties: BucketName: !Sub ${BucketName}-${AWS::AccountId} BucketEncryption: ServerSideEncryptionConfiguration: - ServerSideEncryptionByDefault: SSEAlgorithm: AES256 PublicAccessBlockConfiguration: BlockPublicAcls: true BlockPublicPolicy: true IgnorePublicAcls: true RestrictPublicBuckets: true ### Firehose DeliveryStream: Type: "AWS::KinesisFirehose::DeliveryStream" Properties: DeliveryStreamName: !Sub aws-waf-logs-${AWS::Region}-${WebACLName} DeliveryStreamType: DirectPut S3DestinationConfiguration: BucketARN: !GetAtt S3Backet.Arn BufferingHints: IntervalInSeconds: 300 SizeInMBs: 5 CompressionFormat: GZIP RoleARN: !GetAtt FirehoseRole.Arn ### IAM Role for firehose FirehoseRole: Type: AWS::IAM::Role Properties: Path: '/' AssumeRolePolicyDocument: Version: 2012-10-17 Statement: - Effect: Allow Principal: Service: - firehose.amazonaws.com Action: - sts:AssumeRole Condition: StringEquals: sts:ExternalId: !Sub ${AWS::AccountId} Policies: - PolicyName: firehose_delivery_role PolicyDocument: Version: "2012-10-17" Statement: - Effect: "Allow" Action: - glue:GetTable - glue:GetTableVersion - glue:GetTableVersions Resource: "*" - Effect: "Allow" Action: - s3:AbortMultipartUpload - s3:GetBucketLocation - s3:GetObject - s3:ListBucket - s3:ListBucketMultipartUploads - s3:PutObject Resource: - !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId} - !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/* - arn:aws:s3:::%FIREHOSE_BUCKET_NAME% - arn:aws:s3:::%FIREHOSE_BUCKET_NAME%/* - Effect: "Allow" Action: - lambda:InvokeFunction - lambda:GetFunctionConfiguration Resource: - !Sub arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:%FIREHOSE_DEFAULT_FUNCTION%:%FIREHOSE_DEFAULT_VERSION% - Effect: "Allow" Action: - kms:GenerateDataKey - kms:Decrypt Resource: - !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:alias/aws/s3 Condition: StringEquals: kms:ViaService: !Sub s3.${AWS::Region}.amazonaws.com StringLike: kms:EncryptionContext:aws:s3:arn: - !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/* - !Sub arn:aws:s3:::${BucketName}-${AWS::AccountId}/%FIREHOSE_BUCKET_PREFIX%* - Effect: "Allow" Action: - logs:PutLogEvents Resource: - !Sub arn:aws:logs:${AWS::Region}:${AWS::AccountId}:log-group:/aws/kinesisfirehose/aws-waf-logs-${AWS::Region}-${WebACLName}:log-stream:* - Effect: "Allow" Action: - kinesis:DescribeStream - kinesis:GetShardIterator - kinesis:GetRecords Resource: - !Sub arn:aws:kinesis:${AWS::Region}:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME% - Effect: "Allow" Action: - kms:Decrypt Resource: - !Sub arn:aws:kms:${AWS::Region}:${AWS::AccountId}:key/%SSE_KEY_ID% Condition: StringEquals: kms:ViaService: kinesis.%REGION_NAME%.amazonaws.com StringLike: kms:EncryptionContext:aws:kinesis:arn: !Sub arn:aws:kinesis:%REGION_NAME%:${AWS::AccountId}:stream/%FIREHOSE_STREAM_NAME% ### WAF WebACL WebACL: Type: AWS::WAFv2::WebACL Properties: Name: !Ref WebACLName DefaultAction: Allow: {} Scope: REGIONAL VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: !Ref WebACLName SampledRequestsEnabled: false Rules: - Name: AWS-AWSManagedRulesAmazonIpReputationList Priority: 1 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesAmazonIpReputationList OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: AWSManagedRulesAmazonIpReputationList SampledRequestsEnabled: false - Name: AWS-AWSManagedRulesCommonRuleSet Priority: 2 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesCommonRuleSet OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: AWSManagedRulesCommonRuleSet SampledRequestsEnabled: false - Name: AWS-AWSManagedRulesKnownBadInputsRuleSet Priority: 3 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesKnownBadInputsRuleSet OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: AWSManagedRulesKnownBadInputsRuleSet SampledRequestsEnabled: false - Name: AWS-AWSManagedRulesLinuxRuleSet Priority: 4 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesLinuxRuleSet OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: AWSManagedRulesLinuxRuleSet SampledRequestsEnabled: false - Name: AWS-AWSManagedRulesUnixRuleSet Priority: 5 Statement: ManagedRuleGroupStatement: VendorName: AWS Name: AWSManagedRulesUnixRuleSet OverrideAction: Count: {} VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: AWSManagedRulesUnixRuleSet SampledRequestsEnabled: false - Action: Count: {} Name: Custom-IPaddress-BlackList Priority: 0 Statement: IPSetReferenceStatement: Arn: !GetAtt IPBlackList.Arn VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: Custom-IPaddress-BlackList SampledRequestsEnabled: false - Action: Count: {} Name: Custom-Ratebased Priority: 6 Statement: RateBasedStatement: AggregateKeyType: IP Limit: 100 ScopeDownStatement: NotStatement: Statement: IPSetReferenceStatement: Arn: !GetAtt IPWhiteList.Arn VisibilityConfig: CloudWatchMetricsEnabled: true MetricName: Custom-Ratebased SampledRequestsEnabled: false IPWhiteList: Type: "AWS::WAFv2::IPSet" Properties: Name: Custom-ipaddress-whitelist Scope: REGIONAL IPAddressVersion: IPV4 Addresses: - nnn.nnn.nnn.nnn/32 IPBlackList: Type: "AWS::WAFv2::IPSet" Properties: Name: Custom-ipaddress-blacklist Scope: REGIONAL IPAddressVersion: IPV4 Addresses: - nnn.nnn.nnn.nnn/32
参考
Announcing AWS Managed Rules for AWS WAF AWS Managed Rules for AWS WAF CloudFormation User Guide
以上、吉井 亮 がお届けしました。